package com.scsoft.dataacceptor.server;

import com.hanwei.uap.common.data.check.LRC8;
import com.hanwei.uap.common.util.ByteUtil;
import com.hanwei.uap.common.util.DateUtil;
import com.hanwei.uap.common.util.StringX;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.scsoft.dataacceptor.common.Water;
import com.scsoft.dataacceptor.mongo.MongoCollectData;
import com.scsoft.dataacceptor.util.TimeUtil;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.Date;

public class WaterServerHandler extends SimpleChannelInboundHandler<ByteBuf> {
    private static final Logger log = LoggerFactory.getLogger("water");
    private MongoCollectData mongoCollectData;

    public WaterServerHandler(MongoCollectData mongoCollectData) {
        super();
        this.mongoCollectData = mongoCollectData;
    }

    private byte[] getWaterData(ByteBuf msg) {
        byte[] data = new byte[msg.readableBytes()];

        msg.readBytes(data);
        String hexData = ByteUtil.byte2hex(data);
        log.info("the xiangyun data length:{}, the data hex={}", data.length, hexData);
        if(data.length<19+15) {
            log.warn("data length less than 34(19 is xiangyun base data,15 is water base data length)");
            return null;
        }
        if ((data[0] & 0xFF) != 0xAA || (data[data.length - 1] & 0xFF) != 0xC3) {
            log.warn("unvalid data, it not starts with 0xAA and ends with 0xC3,channel will be closed!");
            return null;
        }
        byte lrc8Code = data[data.length - 2];
        byte checkCode = LRC8.getLrc8(data, 1, data.length - 3);
        if (lrc8Code != checkCode) {
            log.warn("xiangyun lrc8 check error,lrc8Code[{}]!=checkCode[{}]", ByteUtil.byte2hex(lrc8Code), ByteUtil.byte2hex(checkCode));
            return null;
        }
        byte[] waterData = ByteUtil.arraycopy(data, 17, data.length - 19);
        return waterData;
    }
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        Date receiveTime = new Date();
        String createTime = TimeUtil.sdf.format(receiveTime);
        String dataTime = TimeUtil.sdf2.format(receiveTime);
        byte[] data = getWaterData(msg);
        if (data == null) {
            ctx.close();
            return;
        }
        if ((data[0] & 0xFF) != 0xFF || (data[data.length - 1] & 0xFF) != 0x0D) {
            log.warn("unvalid data, it not starts with 0xFF and ends with 0x0D,channel will be closed!");
            ctx.close();
            return;
        }
        byte lrc8Code = data[data.length - 2];
        byte checkCode = LRC8.getLrc8(data, 1, data.length - 3);
        if (lrc8Code != checkCode) {
            log.warn("water lrc8 check error,lrc8Code[{}]!=checkCode[{}]", ByteUtil.byte2hex(lrc8Code), ByteUtil.byte2hex(checkCode));
            ctx.close();
            return;
        }
        String hexData = ByteUtil.byte2hex(data);
        log.info("the water data length:{}, the data hex={}", data.length, hexData);
        byte[] protocolVersionBytes = ByteUtil.arraycopy(data, 1, 1);
        String protocolVersionHex = ByteUtil.byte2hex(protocolVersionBytes, true);
        byte[] deviceIdBytes = ByteUtil.arraycopy(data, 2, 8);
        long deviceId = ByteUtil.bytes2long(deviceIdBytes);
        String snCode = StringX.int2str(deviceId + "", 10);
        byte[] siteMsgBytes = ByteUtil.arraycopy(data, 10, 1);
        String siteMsgHex = ByteUtil.byte2hex(siteMsgBytes, true);
        byte[] itemNumBytes = ByteUtil.arraycopy(data, 11, 2);
        int itemNum = ByteUtil.bytes2short(itemNumBytes);
        log.info("协议版本号：{}， 站点序列号：{}， 站点消息：{}， 数据个数：{}, ", protocolVersionHex, deviceId, siteMsgHex, itemNum);
        for (int i = 0; i < itemNum; i++) {
            byte[] itemIdBytes = ByteUtil.arraycopy(data, 13 + 19 * i, 2);
            int itemId = ByteUtil.bytes2short(itemIdBytes);
            byte[] itemNameBytes = ByteUtil.arraycopy(data, 15 + 19 * i, 1);
            String itemNameHex = ByteUtil.byte2hex(itemNameBytes, true);
            String itemCode = Water.itemNameMap.get(protocolVersionHex + "_" + itemNameHex);
            byte[] itemAttrsBytes = ByteUtil.arraycopy(data, 16 + 19 * i, 1);
            String itemAttrs = StringX.bytes2binary(itemAttrsBytes, "");
            String isUse = itemAttrs.charAt(7) + "";
            String isShowTemp = itemAttrs.charAt(6) + "";
            String isShowAlarm = itemAttrs.charAt(5) + "";
            String isShowMsg = itemAttrs.charAt(4) + "";
            byte[] itemValueBytes = ByteUtil.arraycopy(data, 17 + 19 * i, 4);
            double itemValue = ByteUtil.bytes2int(itemValueBytes) / 100d;
            byte[] itemUnitBytes = ByteUtil.arraycopy(data, 21 + 19 * i, 1);
            String itemUnitHex = ByteUtil.byte2hex(itemUnitBytes, true);
            String itemUnit = Water.itemUnitMap.get(itemUnitHex);
            byte[] itemTempBytes = ByteUtil.arraycopy(data, 22 + 19 * i, 2);
            double itemTemp = ByteUtil.bytes2short(itemTempBytes) / 10d;
            byte[] itemTimeBytes = ByteUtil.arraycopy(data, 24 + 19 * i, 6);
            String itemTime = "20" + StringX.int2str(itemTimeBytes[0] + "", 2)
                    + StringX.int2str(itemTimeBytes[1] + "", 2) + StringX.int2str(itemTimeBytes[2] + "", 2)
                    + StringX.int2str(itemTimeBytes[3] + "", 2) + StringX.int2str(itemTimeBytes[4] + "", 2)
                    + StringX.int2str(itemTimeBytes[5] + "", 2);
            if (!DateUtil.isValidDate(DateUtil.DF_SALL14, itemTime)) {
                log.warn("{}采集时间无效：{}", itemCode, itemTime);
                continue;
            }

            Date collectDate = TimeUtil.sdf2.parse(itemTime);
            String monitorTime = TimeUtil.sdf.format(collectDate);

            /*Date currentDate = DateUtil.getCurrentDate();
            long diffMs = collectDate.getTime() - currentDate.getTime();//时间差
            if (diffMs < -365 * 24 * 3600 * 1000 || diffMs > 3600 * 1000) {
                log.warn("{}采集时间无效：{}", itemCode, itemTime);
                continue;
            }*/

            byte[] alarmInfoBytes = ByteUtil.arraycopy(data, 30 + 19 * i, 1);
            String alarmInfoHex = ByteUtil.byte2hex(alarmInfoBytes, true);
            byte[] deviceInfoBytes = ByteUtil.arraycopy(data, 31 + 19 * i, 1);
            String deviceInfoHex = ByteUtil.byte2hex(deviceInfoBytes, true);
            DBObject source = new BasicDBObject();
            source.put("SNCODE", snCode);
            source.put("MONITORCODE", "202");
            source.put("CREATETIME", createTime);
            source.put("MONITORTIME", monitorTime);
            source.put("DATASOURCE", "GPRS");
            source.put("DATATYPE", "30s");
            source.put("ITEMID", itemId + "");
            source.put("ITEMCODE", itemCode);
            source.put("ITEMVALUE", itemValue + "");
            source.put("ITEMUNIT", itemUnit);
            source.put("ITEMTEMP", itemTemp + "");
            source.put("ATTRS", itemAttrs);
            source.put("ISUSE", isUse);
            source.put("ISSHOWTEMP", isShowTemp);
            source.put("ISSHOWALARM", isShowAlarm);
            source.put("ISSHOWMSG", isShowMsg);
            source.put("ALARMINFO", alarmInfoHex);
            source.put("DEVICEINFO", deviceInfoHex);
            String month = dataTime.substring(0, 6);
            mongoCollectData.saveSrcData(month, source);
            log.info("保存成功:{}", source.toString());
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        log.warn("会话异常", cause);
        ctx.close();
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        SocketAddress address = ctx.channel().remoteAddress();
        log.info("SocketAddress.toString()=ctx.channel().remoteAddress().toString():{}", address.toString());
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        if (IdleStateEvent.class.isAssignableFrom(evt.getClass())) {
            log.info("HostName:{},HostString:{},Port:{},HostAddress:{}", insocket.getHostName(), insocket.getHostString(), insocket.getPort(), insocket.getAddress().getHostAddress());
            log.info(insocket.getAddress().getClass().getName());
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE)
                log.info("{} read idle, the channel is timeout and will be closed!", insocket.getPort());
            else if (event.state() == IdleState.WRITER_IDLE)
                log.info("{} write idle, the channel is timeout and will be closed!", insocket.getAddress().getHostAddress());
            else if (event.state() == IdleState.ALL_IDLE)
                log.info("{} all idle, the channel is timeout and will be closed!", insocket.getAddress().getHostAddress());
            ctx.close();
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }
}